JS 30 是由加拿大的全端工程師 Wes Bos 免費提供的 JavaScript 簡單應用課程,課程主打 No Frameworks、No Compilers、No Libraries、No Boilerplate 在30天的30部教學影片裡,建立30個JavaScript的有趣小東西。
另外,Wes Bos 也很無私地在 Github 上公開了所有 JS 30 課程的程式碼,有興趣的話可以去 fork 或下載。
使用 JavaScript 實作出能在 HTML Canvas 上用筆刷畫圖的極簡版小畫家。
在 body 內放入 canvas 標籤作為我們的畫布,這裡雖然指定大小為 800*800,但之後會利用 JS 把它改成跟視窗(window)一樣大。
<canvas id="draw" width="800" height="800"></canvas>
取得 canvas 標籤後,將畫布的寬、高,將其改成跟視窗內部的寬、高一樣的數值。
const canvas = document.querySelector('#draw');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas 只是一個空白的畫布,實際上我們需要"取得畫布的渲染環境(rendering context)"才能在渲染環境上作畫。
我們呼叫 canvas 的 getContext() 方法並放入 "2d" 作為參數,順利取得 canvas 的 2D 渲染環境,若是要取得 3D 渲染環境只要放入 "3d" 作為參數就好。
緊接著,我們調整 context 的 strokeStyle 屬性值,更改作畫時的畫筆顏色。
context 的 lineJoin 是用來設定兩條長度不為0的線段如何在接合處連接,把值設定為 "round" 則在相連部分以扇形連接。
context 的 lineCap 是用來設定線段末端的呈現方式,把值設定為 "round" 則在末端部分以扇形呈現。
context 的 lineWidth 用來設定線條的粗細。
const ctx = canvas.getContext('2d');
ctx.strokeStyle = '#BADASS';
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
ctx.lineWidth = 100;
我們希望在視窗下按住滑鼠拖曳時作畫,所以在 canvas 上註冊4個事件監聽器。
觸發'mousedown'事件時,設定 isDrawing = true,表示現在可以作畫;設定 [lastX,lastY] = [e.offsetX,e.offsetY],更新作畫的起始點。
觸發'mousemove'事件時,用 draw() 方法持續的作畫。
觸發'mouseup'、'mouseout'時,皆設定 isDrawing = false,表示在滑鼠沒被按住和滑鼠離開視窗的情況下都停止作畫。
let isDrawing = false;//flag
let lastX = 0;
let lastY = 0;
canvas.addEventListener('mousemove',draw);
canvas.addEventListener('mousedown',(e)=> {
isDrawing = true;
[lastX,lastY] = [e.offsetX,e.offsetY];
}
);
canvas.addEventListener('mouseup',()=> isDrawing = false);
canvas.addEventListener('mouseout',()=> isDrawing = false);
事件處理方法 draw(),在最初就先判斷現在能不能作畫,不能的話(isDrawing = false)就直接返回。
ctx.beginPath() 用來建立一個新的作畫路徑。ctx.moveTo() 用來移動路徑起始點的座標。ctx.lineTo() 用來指定這條路徑的終點座標。ctx.stroke() 將這條路徑描出來。
[lastX,lastY] = [e.offsetX,e.offsetY] 用來不斷更新"此次"按住滑鼠拖曳作畫過程中持續變動的路徑起始點。
function draw(e){
if(!isDrawing) return;
ctx.beginPath();
//start from
ctx.moveTo(lastX,lastY);
//go to
ctx.lineTo(e.offsetX,e.offsetY);
ctx.stroke();
[lastX,lastY] = [e.offsetX,e.offsetY];
}
HSL 的 H(Hue,色相角度),是由 0~360 為止的色相循環,0是紅色、120是綠色、240是藍色,Hue 本身不加單位,詳細說明可以參考下方補充資料 CSS Coke 大大的文章。
宣告 hue 變數指定畫筆顏色,並持續的將 hue + 1,當 hue >= 360時,代表顏色已經循環過一次,故將其重設為0。
宣告 direction 變數作為目前該加粗畫筆還是讓畫筆變細的判斷,direction 的初始值是 true 也就是讓畫筆變粗。第一個 if 判斷目前的畫筆粗細是不是 >= 100 或 <= 1,如果條件符合就將 direction 變為相反值(true 變 false)。在第二個的 if-else 判斷,如果 direction = true 就加粗畫筆、反之則讓畫筆變得更細。
let hue = 0;
let direction = true;
function draw(e){
/*.......省略*/
ctx.strokeStyle = `hsl(${hue},100%,50%)`;
/*.......省略*/
hue++;
if(hue >= 360){
hue = 0;
}
if(ctx.lineWidth >= 100 || ctx.lineWidth <= 1){
direction = !direction;
}
if(direction){
ctx.lineWidth++;
}else{
ctx.lineWidth--;
}
}
最後來介紹一個特別的東西,ctx 的 globalCompositeOperation主要用來處理疊圖(線段重疊)的情況,若將其設定為 'multiply' 則將上層像素與相對應的下層像素相乘,整體效果是讓顏色更加趨於黑色。
ctx.globalCompositeOperation = 'multiply';
globalCompositeOperation)
Canvas 基本用途
CanvasRenderingContext2D.strokeStyle
CanvasRenderingContext2D.lineJoin
CanvasRenderingContext2D.lineCap
CanvasRenderingContext2D.lineWidth
CanvasRenderingContext2D.beginPath()
CanvasRenderingContext2D.moveTo()
CanvasRenderingContext2D.lineTo()
RGB、HSL、Hex 網頁色彩碼,看完這篇全懂了
JS一秒區分clientX,offsetX,screenX,pageX之間關係
CanvasRenderingContext2D.globalCompositeOperation